#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
乳腺癌诊断数据分析与预测（简化版）
使用朴素贝叶斯分类算法实现乳腺癌诊断结果的分类预测
"""

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.preprocessing import LabelEncoder
from sklearn.model_selection import train_test_split, GridSearchCV
from sklearn.naive_bayes import GaussianNB
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score, confusion_matrix, classification_report
import warnings
warnings.filterwarnings('ignore')

# 设置中文显示
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False

# 创建输出目录
import os
if not os.path.exists('output'):
    os.makedirs('output')

def main():
    # 数据读入
    df = pd.read_csv("bc_data1.csv", header=None)
    
    # 设置列名
    cell_feat = ['radius', 'texture', 'perimeter', 'area', 'smoothness', 
                'compactness', 'concavity', 'concave_points', 'symmetry', 
                'fractal_dimension']
    
    all_feat = ['id', 'diagnosis']
    for stat in ['mean_', 'se_', 'largest_']:
        for feat in cell_feat:
            all_feat.append(stat + feat)
    
    df.columns = all_feat
    
    # 将诊断结果编码为数值
    encoder = LabelEncoder()
    df['diagnosis'] = encoder.fit_transform(df['diagnosis'])
    
    # 诊断结果分布可视化
    diagnosis_count = df['diagnosis'].value_counts()
    plt.figure(figsize=(10, 6))
    plt.subplot(1, 2, 1)
    sns.countplot(x='diagnosis', data=df, palette=['lightblue', 'salmon'])
    plt.title('诊断结果分布 (柱状图)')
    plt.xlabel('诊断结果 (0=良性, 1=恶性)')
    plt.ylabel('数量')
    
    plt.subplot(1, 2, 2)
    plt.pie(diagnosis_count.values, labels=['良性', '恶性'], autopct='%1.1f%%', 
            colors=['lightblue', 'salmon'], startangle=90, explode=[0.05, 0])
    plt.title('诊断结果分布 (饼图)')
    plt.tight_layout()
    plt.savefig('output/diagnosis_distribution.png', dpi=300)
    
    # 特征相关性分析
    mean_features = [col for col in df.columns if 'mean_' in col]
    correlation = df[mean_features].corr()
    plt.figure(figsize=(14, 12))
    sns.heatmap(correlation, annot=True, cmap='coolwarm', fmt='.2f')
    plt.title('均值特征相关性热图')
    plt.tight_layout()
    plt.savefig('output/correlation_heatmap.png', dpi=300)
    
    # 准备特征和目标变量
    x = df.iloc[:, 2:]
    y = df['diagnosis']
    
    # 划分训练集和测试集
    x_train, x_test, y_train, y_test = train_test_split(
        x, y, test_size=0.3, random_state=42, stratify=y
    )
    
    # 模型训练
    gnb = GaussianNB()
    gnb.fit(x_train, y_train)
    
    # 模型评价
    y_pred = gnb.predict(x_test)
    accuracy = accuracy_score(y_test, y_pred)
    precision = precision_score(y_test, y_pred)
    recall = recall_score(y_test, y_pred)
    f1 = f1_score(y_test, y_pred)
    
    print("="*50)
    print("乳腺癌诊断数据分析与预测")
    print("="*50)
    print(f"准确率: {accuracy:.6f}")
    print(f"精确率: {precision:.6f}")
    print(f"召回率: {recall:.6f}")
    print(f"F1值: {f1:.6f}")
    
    # 混淆矩阵可视化
    cm = confusion_matrix(y_test, y_pred)
    plt.figure(figsize=(8, 6))
    sns.heatmap(cm, annot=True, fmt='d', cmap='Blues')
    plt.title('混淆矩阵')
    plt.xlabel('预测标签')
    plt.ylabel('真实标签')
    plt.savefig('output/confusion_matrix.png', dpi=300)
    
    # 模型调参
    param_grid = {'var_smoothing': [1e-7, 1e-8, 1e-9, 1e-10, 1e-11, 1e-12]}
    grid_search = GridSearchCV(GaussianNB(), param_grid, cv=5)
    grid_search.fit(x_train, y_train)
    
    print(f"\n最佳参数: {grid_search.best_params_}")
    
    # 使用最佳参数的模型进行预测
    best_gnb = grid_search.best_estimator_
    tuned_y_pred = best_gnb.predict(x_test)
    
    # 计算调优后的评价指标
    tuned_accuracy = accuracy_score(y_test, tuned_y_pred)
    tuned_precision = precision_score(y_test, tuned_y_pred)
    tuned_recall = recall_score(y_test, tuned_y_pred)
    tuned_f1 = f1_score(y_test, tuned_y_pred)
    
    print(f"调优后准确率: {tuned_accuracy:.6f}")
    print(f"调优后精确率: {tuned_precision:.6f}")
    print(f"调优后召回率: {tuned_recall:.6f}")
    print(f"调优后F1值: {tuned_f1:.6f}")
    
    # 比较调参前后的性能
    plt.figure(figsize=(10, 6))
    metrics = ['准确率', '精确率', '召回率', 'F1值']
    before = [accuracy, precision, recall, f1]
    after = [tuned_accuracy, tuned_precision, tuned_recall, tuned_f1]
    
    x = np.arange(len(metrics))
    width = 0.35
    
    plt.bar(x - width/2, before, width, label='调参前', color='lightblue')
    plt.bar(x + width/2, after, width, label='调参后', color='lightgreen')
    
    plt.xlabel('评价指标')
    plt.ylabel('得分')
    plt.title('调参前后模型性能比较')
    plt.xticks(x, metrics)
    plt.legend()
    plt.ylim(0, 1.0)
    
    for i, v in enumerate(before):
        plt.text(i - width/2, v + 0.02, f'{v:.4f}', ha='center')
    
    for i, v in enumerate(after):
        plt.text(i + width/2, v + 0.02, f'{v:.4f}', ha='center')
    
    plt.tight_layout()
    plt.savefig('output/model_comparison.png', dpi=300)
    
    print("\n分析完成，结果已保存在output目录下。")

if __name__ == "__main__":
    main()
